package com.palm.vue.compiler;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VueCompressor implements ICompiler {
    private static Pattern templatePattern = Pattern.compile("<template\\s*>",Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
    private static Pattern scriptPattern = Pattern.compile("(?:<script\\s*>|<script type=['\"].+['\"]\\s*>|<script lang=['\"].+['\"]\\s*>)(.*?)</script>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    private static Pattern stylePattern = Pattern.compile("<style[^>()]*?>(.+)</style>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    private static Pattern importPattern = Pattern.compile("import\\s+([\\{\\w\\,\\}\\s]+\\s+from\\s+)?(['\"][\\.\\-\\/\\w]+['\"])",Pattern.DOTALL );
    private int mode;
    Invocable invocable;
    public VueCompressor(int mode){
        this.mode =mode;
        if(mode==2){
            try {
                ScriptEngineManager manager = new ScriptEngineManager();
                ScriptEngine engine = manager.getEngineByName("js");
                invocable = (Invocable) engine;
                engine.put("jct", new JavaContext());
                Object atob = engine.getContext().getAttribute("atob");
                if (atob == null) {
                    engine.eval("function atob(src){return jct.atob(src);}");
                }
                Object btoa = engine.getContext().getAttribute("btoa");
                if (btoa == null) {
                    engine.eval("function btoa(src){return jct.btoa(src);}");
                }
                engine.eval(VueCompiler.read(VueCompiler.class.getResourceAsStream("/uglifyjs.js")));
                engine.eval("function compress(src){ return UglifyJS.minify(src).code;}");
            }catch (ScriptException e){
                e.printStackTrace();
            }
        }
    }
    public  String compile(String source){
        String script = "return {}";
        String style = null;
        String template = null;
        StringBuilder append=new StringBuilder();
        Matcher templateMatcher = templatePattern.matcher(source);
        if(templateMatcher.find()){
            int start=templateMatcher.end();
            int end = source.lastIndexOf("</template>");
            template = source.substring(start, end);
            template=toStrBlock(compressTemplate(template));
            append.append("template:"+template);
        }
        Matcher scriptMatcher = scriptPattern.matcher(source);
        if(scriptMatcher.find()){
            script=scriptMatcher.group(1);
            script = script.replaceFirst("(^|\\s)export\\s+default\\s*\\{", "\nreturn{");
            Matcher importMatcher=importPattern.matcher(script);
            StringBuffer sb = new StringBuffer() ;
            while (importMatcher.find()){
                String g1=importMatcher.group(1);
                String g2=importMatcher.group(2);
                if(g1==null){
                    importMatcher.appendReplacement(sb,"require(" + g2 + ")");
                }else{
                    String name = g1.replaceAll("\\s+from\\s+$", "");
                    importMatcher.appendReplacement(sb,"const " + name + " = require(" + g2 + ")");
                }
            }
            importMatcher.appendTail(sb) ;
            script=compressJs(sb.toString());
        }
        Matcher styleMatcher = stylePattern.matcher(source);
        if(styleMatcher.find()){
            style=styleMatcher.group(1);
            style=toStrBlock(compressCss(style));
            if(append.length()>0){
                append.append(",");
            }
            append.append("style:"+style);
        }
        String partA="_$(()=>{"+script+"})";
        if(mode ==2){
            partA=compress(partA);
            partA=partA.substring(0,partA.length()-1);
        }
       return "define(((_)=>(require) =>_$("+partA+", _))({"+append+"}))";
    }
    private String compress(String source){
        try {
            String ret = (String) invocable.invokeFunction("compress", source);
            return ret;
        } catch (ScriptException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
    public String compile(InputStream in){
        String src=VueCompiler.read(in);
        return compile(src);
    }
    private static String toStrBlock(String source){
        return "`"+source.replaceAll("\\\\","\\\\").replaceAll("`","\\`")+"`";
    }

    private static String compressCss(String css){
        return new CssCompressor(css).minify();
    }
    private static String compressTemplate(String template){
        return HtmlCompressor.compress(template);
    }
    private static String compressJs(String js){
        return new JsCompressor(js).minify();
    }
}
